/*****************************************************************************
 * $LastChangedDate: 2009-11-22 22:39:11 -0500 (Sun, 22 Nov 2009) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   HUD.
 * @remarks
 * Arcane magic and kludges:
 * -------------------------
 * - Constant window size:
 *   Indeed, the width,height of the HUD remains constant at 1280,1024.
 *   Intentionally, there is no listener for a window-resize event,
 *   since OSG will automatically scale osgText strings.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define HUD_HUD_CC 1
#include "base/module.hh"
using namespace base;
#include "gfx/module.hh"
#include "gfx/osg.hh"
using namespace gfx;
#include "graph/module.hh"
using namespace graph;
#include "view/module.hh"
#include "view/window.hh"
using namespace view;
#include "hud/module.hh"
#include "hud/hud_box.hh"
#include "hud/hud.hh"

#include <osg/Version>

#if OSG_VERSION_LESS_THAN(3,0,0)
#include <osg/CameraNode>
#else
#include <osg/Camera>
#endif

namespace hud {

////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////  Hud  //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

Hud* Hud::msInstance;

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
Hud::Hud( void )
#if OSG_VERSION_LESS_THAN(3,0,0)
:   mCamera(new osg::CameraNode),
#else
:   mCamera(new osg::Camera),
#endif
    mGroup(new osg::Group),
    mHudBoxes()
{
ASSERT_RUN_ONCE;

    // Stateset (such as disabled fog) has no effect on osg::CameraNode,
    // so an intermediate osg::Group is used to hold nodes of the HUD.
    mCamera->addChild( mGroup.get() );
    mGroup->getOrCreateStateSet()->setMode( GL_LIGHTING, osg::StateAttribute::OFF );
    mGroup->getOrCreateStateSet()->setMode( GL_FOG,      osg::StateAttribute::OFF );
    mGroup->getOrCreateStateSet()->setMode( GL_BLEND,    osg::StateAttribute::OFF );

    // Attach to Window (where to render).
    GET_WINDOW().OsgAddCamera( mCamera.get(), Window::eCamera_2D );

    // Set 2D viewport.
    const WidthHeight<int> wh = GET_WINDOW().GetWindowSize();
    mCamera->setViewport( 0, 0, wh.w, wh.h );

    // Set a 2D projection matrix.
    mCamera->setProjectionMatrix( osg::Matrix::ortho2D( HUD_ORTHO_X1,
                                                        HUD_ORTHO_X2,
                                                        HUD_ORTHO_Y1,
                                                        HUD_ORTHO_Y2 ) );
    // Set the view matrix.
    mCamera->setReferenceFrame( osg::Transform::ABSOLUTE_RF );
    mCamera->setViewMatrix( osg::Matrix::identity() );

    // Only clear the depth buffer.
    mCamera->setClearMask( GL_DEPTH_BUFFER_BIT );

    // Draw subgraph after main camera view.
#if OSG_VERSION_LESS_THAN(3,0,0)
    mCamera->setRenderOrder( osg::CameraNode::POST_RENDER );
#else
    mCamera->setRenderOrder( osg::Camera::POST_RENDER );
#endif
}

Hud::~Hud()
{
    // NOP
}

/*****************************************************************************
 * Enable/disable HUD.
 *****************************************************************************/
void
Hud::Enable( const bool enable )
{
    mCamera->setNodeMask( enable ? ~0 : 0 );
}

/*****************************************************************************
 * Attach a node to HUD.
 *****************************************************************************/
void
Hud::Attach( RefPtr<Node> node )
{
    mGroup->addChild( node.get() );
}

/*****************************************************************************
 * Attach a HudBox to HUD.
 *****************************************************************************/
void
Hud::Attach( shptr<HudBox> hudBox )
{
CHECK_TYPESIG(hudBox,TYPESIG_HUD_BOX);

    // HudBox automatically draws itself and it doesn't have any OSG nodes.
    // But that is hidden from clients: the Attach() interface should be used anyway.
    // Although Hud itself does nothing with a HudBox, Hud has to contain them,
    // as Attach() implies containment which keeps shptr from deleting them.
    mHudBoxes.insert( hudBox );
}

/*****************************************************************************
 * Detach a node from HUD.
 *****************************************************************************/
void
Hud::Detach( RefPtr<Node> node )
{
    mGroup->removeChild( node.get() );
}

/*****************************************************************************
 * Detach a HudBox from HUD.
 *****************************************************************************/
void
Hud::Detach( shptr<HudBox> hudBox )
{
CHECK_TYPESIG(hudBox,TYPESIG_HUD_BOX);

    // When a HudBox has zero references, it will uninstall
    // its OSG update callback (low-level drawing method).
    mHudBoxes.erase( hudBox );
}

} // namespace hud
